package main

import (
	"fmt"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
	"golang.org/x/crypto/bcrypt"
	"time"
)

var  timeLayoutStr = "2006-01-02 15:04:05" //go中的时间格式化必须是这个时间
type Users struct {
	//固定字段（id,UpdatedAt,DeletedAt,CreatedAt）
	//gorm.Model
	Name string
	Age  int
	Birthday time.Time
}

type count struct {
	//固定字段（id,UpdatedAt,DeletedAt,CreatedAt）
	//gorm.Model

	Total  int
}
//type Userss struct {
//    gorm.Model
//    Name string
//    Age sql.NullInt64
//
//}
type Result struct {
	Date  time.Time
	Total int64
}



var Db *gorm.DB

func init() {
	db,err := gorm.Open("mysql","root:123456@tcp(127.0.0.1:3306)/test?charset=utf8&parseTime=True&loc=Local")
	// 如果设置禁用表名复数形式属性为 true，`User` 的表名将是 `user`
	db.SingularTable(true)
	if err !=nil {
		panic(err)
	}
	err = db.DB().Ping()
	if err!=nil {
		db.Close()
	}
	Db =db
}

func main() {
	var cstSh, _ = time.LoadLocation("Asia/Shanghai") //上海
	birthday := time.Now().In(cstSh).Format(timeLayoutStr)

	time, _ := time.Parse(timeLayoutStr, birthday) //string转time
	fmt.Println(time)
	user := &Users{
		Name:     "huangyuzhou12",
		Age:      20,
		Birthday: time,
	}
	//创建数据
	// .Table指定表
	Db.Create(&user)
	//查找数据(根据主键查询)
	// SELECT * FROM users WHERE name = "jinzhu" AND age = 20 LIMIT 1;
	Db.First(&user)
	fmt.Println(user)
	//// SELECT * FROM users WHERE name = "jinzhu" AND age = 20;
	Db.Find(map[string]interface{}{"name": "huangyuzhou12"}, &user)

	//SELECT * FROM users WHERE id IN (20, 21, 22);
	Db.Find([]int{20, 21, 22}, &user)
	Db.Find(&user, "name=?", "huangyuzhou12")
	//获取最后一条数据
	Db.Last(&user)
	fmt.Println(user, )
	// 更新
	Db.Model(&user).Update("name", "huangyuzhou44")
	// 删除
	Db.Delete(&user, "name=?", "huangyuzhou12")
	Db.Not("name", "jinzhu").First(&user)
	// SELECT * FROM users;
	Db.Not([]int64{}).First(&user)
	//map // SELECT * FROM users WHERE name = 'jinzhu' OR name = 'jinzhu 2';
	Db.Where("name = 'jinzhu'").Or(map[string]interface{}{"name": "jinzhu 2"}).Find(&user)

	/**
	NOTE 当通过struct进行查询的时候，GORM 将会查询这些字段的非零值， 意味着你的字段包含 0， ''，
	false 或者其他 零值, 将不会出现在查询语句中，
	*/
//-----------------------------------------------------------------------------
//-----------------------------FirstOrInit(初始化)----------
	//获取第一条匹配的记录，或者通过给定的条件下初始一条新的记录（仅适用与于 struct 和 map 条件）
	Db.FirstOrInit(&user,Users{Name:"MMMM"})
	Db.FirstOrInit(&user, map[string]interface{}{"name":"huangyuzhou12"})

	//如果未找到记录，则使用参数初始化 struct
	//------------------------------Attrs--------------------
	//未查询到
	Db.Where(Users{Name:"MMMM"}).Attrs(Users{Age:20}).FirstOrInit(&user)
	//// SELECT * FROM USERS WHERE name = 'non_existing';
	//// user -> User{Name: "non_existing", Age: 20}

	//查询到
	Db.Where(Users{Name:"huangyuzhou12"}).Attrs(Users{Age:20}).FirstOrInit(&user)
	//// SELECT * FROM USERS WHERE name = jinzhu';
	//// user -> User{Id: 111, Name: "Jinzhu", Age: 20}

	//无论是否查询到数据，都将参数赋值给 struct
	// -------------------------- Assign------------------
	Db.Where(Users{Name:"huangyuzhou12"}).Assign(Users{Age:20}).FirstOrInit(&user)

//-----------------------------------------------------------------------------
//-----------------------------FirstOrCreate（新增）----------
   //获取第一条匹配的记录，或者通过给定的条件创建一条记录 （仅适用与于 struct 和 map 条件）
   Db.FirstOrCreate(&user,Users{Name:"non_existing"})
	//// INSERT INTO "users" (name) VALUES ("non_existing");
	//// user -> User{Id: 112, Name: "non_existing"}

// ------------------------------------高级查询----------------------------------------
 // SELECT * FROM "orders"  WHERE "orders"."deleted_at" IS NULL AND (amount > (SELECT AVG(amount) FROM "orders"  WHERE (state = 'paid')));
  Db.Where("age >?",Db.Table("order").Select("AVG(age)").Where("state =?","paid").Find(&user))
	//查询
	//select name,age from usrer
    Db.Select("name,age").Find(&user)
	Db.Select([]string{"name","age"}).Find(&user)
    // SELECT COALESCE(age,'42') FROM users;
	Db.Select("COALESCE(age,?)", 42).Rows()
	//order by
	//select* from users order by age desc name
	Db.Order("age desc，name").Find(&user)
	//limit
	Db.Limit(3).Find(&user)
	//count
	Db.Where("name =?","huangyuzhou44").Or("name =?","huangyuzhou12").Find(&user).Count(1)
	Db.Model(&user).Where("name =?","huangyuzhou44").Count(1)


	results := &Result{
		Date:  time,
		Total: 0,
	}
	Db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Having("sum(amount) > ?", 100).Scan(&results)

	// 多个关联查询
	Db.Joins("JOIN emails ON emails.user_id = users.id AND emails.email = ?", "jinzhu@example.org").Joins("JOIN credit_cards ON credit_cards.user_id = users.id").Where("credit_cards.number = ?", "411111111111").Find(&user)
    //distinct

    //Scan
    Db.Table("users").Select("name","age").Where("name=?","aaa").Scan(&results)
	Db.Raw("select name,age from users where name =?",3).Scan(&results)

	var ages []int64
	Db.Find(&user).Pluck("age",&ages)


	//----------------------------------------
	//以下的更新操作更新时会执行模型的 BeforeUpdate 和 AfterUpdate 方法，来更新 UpdatedAt 时间戳，并且保存他的 关联。如果你不想执行这些操作，可以使用 UpdateColumn，UpdateColumns 方法
	user.Name ="Name1"
	user.Age=222
	//更新所有
	// UPDATE users SET name='jinzhu 2', age=100, birthday='2016-01-01', updated_at = '2013-11-17 21:34:10' WHERE id=111;
	Db.Save(&user)
	//更新已修改字段
	Db.Model(&user).Where("active = ?", true).Update("name","hello")
	Db.Model(&user).Where("active = ?", true).Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false})
	// 警告： 当使用结构体更新的时候, GORM 只会更新那些非空的字段
	// 例如下面的更新，没有东西会被更新，因为像 "", 0, false 是这些字段类型的空值
	Db.Model(&user).Update(Users{Name: "", Age: 0})
	//更新选中的字段
    Db.Select("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false})
	Db.Omit("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false})
   //更新列钩子方法
   Db.Model(&user).UpdateColumn("name","lisi")

	// ------------------------------------------批量更新------------------------------------
	Db.Table("users").Where("id in(?)", []int{10,11}).Updates(map[string]interface{}{"name": "hello", "age": 18})
	// 使用 `RowsAffected` 获取更新影响的记录数
	 results1 := Db.Model(Users{}).Updates(Users{Name: "hello", Age: 18})
	 //获取更新条数
	 total :=  results1.RowsAffected
	 error := results1.Error
	 fmt.Println(total,error)

	 //带有表达式的 SQL 更新
	 Db.Model(&user).Update("price",gorm.Expr("price *?+?",2,18))
	 // UPDATE "products" SET "quantity" = quantity - 1 WHERE "id" = '2' AND quantity > 1;
	 Db.Model(&user).Where("quantity > 1").UpdateColumn("quantity", gorm.Expr("quantity - ?", 1))

	 // ----------------------------删除-----------------------------------
	//删除所有匹配的记录
	 Db.Where("email LIKE ?", "%jinzhu%").Delete(Users{})
	 //软删除
	 //如果模型中有 DeletedAt 字段，它将自动拥有软删除的能力！当执行删除操作时，数据并不会永久的从数据库中删除，而是将 DeletedAt 的值更新为当前时间。
	 Db.Unscoped().Where("age",20).Find(&user)
	// 使用 Unscoped 方法永久删除记录
	 Db.Unscoped().Delete(&user)
    // ---------------------------- Belongs To ---------------------------------

	//在钩子中设置字段值

	//　钩子是一个在 插入 / 查询 / 更新 / 删除 之前或之后被调用的方法。
	//　　在一个模型中定义特殊的方法，它将会在插入，更新，查询，删除的时候被自动调用，如果任何的回调抛出错误，GORM 将会停止将要执行的操作并且回滚当前的改变。

	//func (users *Users)BeforeCreate(scope gorm.Scope) error  {
	//	scope.SetColumn("ID", uuid.New() )
	//	return nil
	//}
}



//在钩子函数中更新值
func (user *User) BeforeSave(tx *gorm.Scope) (err error) {
	if pw, err := bcrypt.GenerateFromPassword(user.Password, 0); err == nil {
		tx.SetColumn("EncryptedPassword", pw)
	}
	return err
}

